package cn.zly.spring.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.zly.spring.beans.PropertyValue;
import cn.zly.spring.beans.PropertyValues;
import cn.zly.spring.beans.factory.BeansException;
import cn.zly.spring.beans.factory.config.AutowireCapableBeanFactory;
import cn.zly.spring.beans.factory.config.BeanDefinition;
import cn.zly.spring.beans.factory.config.BeanPostProcessor;
import cn.zly.spring.beans.factory.config.BeanReference;
import java.lang.reflect.Constructor;

/**
 * @author zhanglianyong
 * @date 2024/12/30
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory  {

	private InstantiationStrategy instantiationStrategy = new CglibSubClassingInstantiationStrategy();


	@Override
	protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
		Object bean = null;
		try {
			bean = createBeanInstance(beanDefinition, beanName, args);
			// 填充属性
			applyPropertyValues(beanName, bean, beanDefinition);
		} catch (Exception e) {
			throw new BeansException("实例化Bean失败 ", e);
		}
		addSingleton(beanName, bean);
		return bean;
	}

	/**
	 * 填充属性
	 *
	 * @param beanName
	 * @param bean
	 * @param beanDefinition
	 */
	protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
		try {
			PropertyValues propertyValues = beanDefinition.getPropertyValues();
			for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
				String name = propertyValue.getName();
				Object value = propertyValue.getValue();

				if (value instanceof BeanReference) {
					BeanReference beanReference = (BeanReference) value;
					value = getBean(beanReference.getBeanName());
				}
				// 属性填充
				BeanUtil.setFieldValue(bean, name, value);
			}
		} catch (Exception e) {
			throw new BeansException("设置Bean属性失败 [ " + beanName + "]", e);
		}

	}

	protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
		Constructor constructor = null;
		Class beanClass = beanDefinition.getBeanClass();
		Constructor[] declaredConstructors = beanClass.getDeclaredConstructors();
		for (Constructor declaredConstructor : declaredConstructors) {
			if (declaredConstructor.getParameterTypes().length == 0) {
				constructor = declaredConstructor;
			}
			if (null != args && args.length == declaredConstructor.getParameterTypes().length) {
				constructor = declaredConstructor;
				break;
			}
		}

		return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructor, args);
	}

	@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (null == current) return result;
			result = current;
		}
		return result;
	}

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (null == current) return result;
			result = current;
		}
		return result;
	}

	public InstantiationStrategy getInstantiationStrategy() {
		return instantiationStrategy;
	}
}
